let target, show_token, preprocessor, objc, cpp = (
	let target = ref "" in
	let show_token = ref false in
	let preprocessor = ref false in
	let objc = ref false in
	let cpp = ref false in
	let args = Stream.of_list (List.tl (Array.to_list Sys.argv)) in
	while Stream.peek args <> None do
		match Stream.next args with
		| "-p" ->
			preprocessor := true
		| "-v" ->
			show_token := true
		| "-w" ->
			Parser.debug := true
		| "-objc" ->
			objc := true
		| "-c++" ->
			cpp := true
		| "-objc++" ->
			objc := true;
			cpp := true
		| _ as s when s.[0] = '-' ->
			raise (Failure ("Invalid option: " ^ s))
		| _ as path ->
			target := path
	done;
	!target, !show_token, !preprocessor, !objc, !cpp
);;

let preprocess (f: in_channel): string = (
	let command = (
		if objc && cpp then "cpp -dD -x objective-c++" else
		if objc then "cpp -dD -x objective-c" else
		if cpp then "cpp -dD -x c++" else
		"cpp -dD"
	) in
	let (p_in, p_out) as p_io = Unix.open_process command in
	set_binary_mode_in p_in false;
	set_binary_mode_out p_out true;
	begin try
		while true do
			let line = input_line f in
			output_string p_out line;
			output_char p_out '\n'
		done
	with 
	| End_of_file -> ()
	end;
	close_out p_out;
	let result = (
		let r = Buffer.create 0 in
		begin try
			while true do
				let line = input_line p_in in
				Buffer.add_string r line;
				Buffer.add_char r '\n'
			done
		with 
		| End_of_file -> ()
		end;
		Buffer.contents r
	) in
	begin match Unix.close_process p_io with
	| Unix.WEXITED 0 -> result
	| _ -> prerr_string "error: "; prerr_string command; prerr_newline (); exit 1
	end
);;

module Scanner = Scanner.Scanner (Stream);;

let test filename = (
	let ed = ref (("", 0), Parser.Expected []) in
	let rec print_errors (es: (Scanner.text_file_position * Parser.error) list) = (
		let print_error ((p, e): Scanner.text_file_position * Parser.error) = (
			let (f, l) = p in Printf.printf "%s:%d" f l;
			print_string ":";
			Parser.pp_error (Lexical.Token.pp ~paren:true ~quali:false) Format.std_formatter e;
			Format.print_flush ();
			print_newline ()
		) in
		begin match es with
		| e :: er ->
			if e != !ed then (
				print_errors er;
				print_error e;
				ed := e
			)
		| [] -> ()
		end
	) in
	let f = if filename <> "" then open_in filename else stdin in
	let in_stream = (
		if preprocessor then (
			let s = preprocess f in
			Stream.of_string s
		) else (
			Stream.of_channel f
		)
	) in
	let s = Scanner.create ~objc ~cpp filename in_stream in
	let p = Parser.create () in
	let rec loop p = (
		let token, pos = Scanner.next s in
		if show_token then (
			let (f, l) = pos in
			Printf.printf " %s:%d " f l;
			Lexical.Token.pp ~quali:false ~paren:false Format.std_formatter token;
			Format.pp_print_flush Format.std_formatter ();
			print_newline ()
		);
		flush stdout;
		let p = Parser.post pos token p in
		flush stderr;
		if show_token then print_errors (Parser.errors p);
		if token <> Lexical.Token.Eof then loop p else p
	) in
	let p = loop p in
	if filename <> "" then close_in f;
	print_errors (Parser.errors p)
);;

test target;;
